#!/usr/bin/env python
#
#       MajeRe_GUI_rewrite.py
#       
#       Copyright 2009 Brandon Invergo <brandon@brandoninvergo.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.


import sys
import imp
import os
import locale

try:
    import pygtk
    #tell pyGTK, if possible, that we want GTKv2
    pygtk.require("2.0")
except:
    #Some distributions come with GTK2, but not pyGTK
    pass

try:
    import gtk
    import gtk.glade
    
except:
    print "You need to install pyGTK or GTKv2 ",
    print "or set your PYTHONPATH correctly."
    print "try: export PYTHONPATH=",
    print "/usr/local/lib/python2.2/site-packages/"
    sys.exit(1)

import cPickle
from gobject import TYPE_OBJECT, TYPE_STRING, TYPE_BOOLEAN
import MajeRe_Engine
import Error_dialog
import Script_tab
import Data_tab     
import Analyses_tab
import Data_group
import Results_tab
    

class Open_script_dialog:
    encoding = locale.getlocale()[1]
    utf8conv = lambda self, x : unicode(x, self.encoding).encode('utf8')
    
    def on_open_script_open_button(self, widget):
        self.open_script()
            
    def on_open_script_file_activated(self, widget):
        self.open_script()
        
    def open_script(self):
        try:
            opened_script = open(self.dialog.get_filename(),"r")
        except:
            self.error.show_error("Error", "Could not open file")
        content = opened_script.read()
        try:
            content = self.utf8conv(content)  
        except:
            self.error.show_error("Error", "Unsupported file type.")
        else:
            self.main_window.script.script_buffer.set_text(content)
            opened_script.close()
            self.dialog.hide()
            
    def on_open_script_cancel_button(self, widget):
        self.dialog.hide()
        
    def show(self):
        self.dialog.show()
        
    def __init__(self, parent_window, glade_gui):
        self.main_window = parent_window
        self.glade_gui = glade_gui
        self.error = Error_dialog.Error_dialog()
        
        callbacks = {"on_open_script_open_button" : self.on_open_script_open_button,\
        "on_Open_script_file_activated": self.on_open_script_file_activated,\
        "on_open_script_cancel_button" : self.on_open_script_cancel_button}
        
        self.glade_gui.signal_autoconnect(callbacks)
        
        self.dialog = self.glade_gui.get_widget("Open_script_file_chooser")
    
class Export_script_dialog:
    def on_export_script_save_button(self, widget):
        self.export_script()
                
    def on_export_script_file_activated(self, widget):
        self.export_script()
        
    def export_script(self):
        try:
            new_file = open(self.dialog.get_filename(), "w")
        except:
            self.error.show_error("Error", "Could not open file for writing.")
        else:
            try:
                new_file.write(self.script)
            except:
                self.error.show_error("Error", "Could not write to file.")
            else:
                self.dialog.hide()
                
    def on_export_script_cancel_button(self, widget):
        self.dialog.hide()
        
    def show(self, script):
        self.script = script
        self.dialog.show()
        
    def __init__(self, parent_window, glade_gui):
        self.main_window = parent_window
        self.glade_gui = glade_gui
        self.error = Error_dialog.Error_dialog()
        
        callbacks = {"on_export_script_save_button" : self.on_export_script_save_button,\
            "on_Export_script_file_activated": self.on_export_script_file_activated,\
            "on_export_script_cancel_button" : self.on_export_script_cancel_button}
        self.glade_gui.signal_autoconnect(callbacks)
        
        self.dialog = self.glade_gui.get_widget("Export_script_file_chooser")
    
class Save_project_dialog: 
    def on_save_button(self, widget):
        self.project_file = self.dialog.get_filename()
        self.save()
        self.dialog.hide()
    
    def on_save_project_file_activated(self, widget):
        self.project_file = self.dialog.get_filename()
        self.save()
        self.dialog.hide()
    
    def on_cancel_button(self, widget):
        self.dialog.hide()
    
    def show(self):
        self.dialog.show()
        
    def save(self):
        if self.project_file == None:
            self.error.show_error("Error", "Project file ID lost.")
            return
        try:
            new_file = open(self.project_file, "wb")
        except:
            self.error.show_error("Error", "Could not open file for writing.")
            return

        try:
            self.main_window.r_eng.save()
        except MajeRe_Engine.REngException, error_msg:
            self.error.show_error("R Engine error", error_msg.parameter)
            return
        temp_file = open("temp.RData", "rb")
        r_data = temp_file.read()
        temp_file.close()
        os.remove("temp.RData")
        
        script_start_iter = self.main_window.script.script_buffer.get_start_iter()
        script_end_iter = self.main_window.script.script_buffer.get_end_iter()
        script_text = self.main_window.script.script_buffer.get_text(script_start_iter, script_end_iter)
        
        console_start_iter = self.main_window.script.console_buffer.get_start_iter()
        console_end_iter = self.main_window.script.console_buffer.get_end_iter()
        console_text = self.main_window.script.console_buffer.get_text(console_start_iter, console_end_iter)
        
        data_groups_iter = self.main_window.data.dg_treestore.get_iter_first()
        data_groups_list = []
        while data_groups_iter != None:
            data_group = self.main_window.data.dg_treestore.get_value(data_groups_iter, 4)
            data_groups_list.append(data_group)
            data_groups_iter = self.main_window.data.dg_treestore.iter_next(data_groups_iter)
        
        project_data = (self.project_file, r_data, script_text, \
            console_text, data_groups_list)
        
        try:
            cPickle.dump(project_data, new_file, -1)
        except cPickle.PicklingError, pickle_error:
            self.error.show_error("Save error", str(pickle_error))
            return
        self.main_window.saved = True
    
    def __init__(self, parent_window, glade_gui):
        self.project_file = None
        self.main_window = parent_window
        self.glade_gui = glade_gui
        self.error = Error_dialog.Error_dialog()
        
        callbacks = {"on_Save_project_save" : self.on_save_button,\
            "on_Save_project_file_activated": self.on_save_project_file_activated,\
            "on_Save_project_cancel" : self.on_cancel_button}
        self.glade_gui.signal_autoconnect(callbacks)
        
        self.dialog = self.glade_gui.get_widget("Save_project_file_chooser")

class Open_project_dialog:
    def on_open_project_file_activated(self, widget):
        self.open_project()
    
    def on_open_button(self, widget):
        self.open_project()
        
    def open_project(self):        
        filename = self.dialog.get_filename()
        try:
            open_file = open(filename, "rb")
        except:
            self.error.show_error("Error", "Could not open file for writing.")
            return
        try:
           project_data = cPickle.load(open_file)
        except cPickle.UnpicklingError, pickle_error:
           self.error.show_error("Open error", pickle_error.parameter)
           return
        else:
            self.main_window.save_project_dialog.project_file = project_data[0]
            self.main_window.saved = True
            
            data_groups = project_data[4]
            self.main_window.data.dg_treestore.clear()
            for group in data_groups:
                self.main_window.data.add_data_group(group)
                self.main_window.results.update_results(group)
            
            r_data = project_data[1]
            temp_file = open("temp.RData", "wb")
            temp_file.write(r_data)
            temp_file.close()
            
            try:
                self.main_window.r_eng.load()
            except MajeRe_Engine.REngException, error_msg:
                self.error.show_error("R Engine error", error_msg.parameter)
                return
            os.remove("temp.RData")
            self.main_window.data.update_data_view()
            
            
            script_text = project_data[2]
            self.main_window.script.script_buffer.set_text(script_text)
            
            console_text = project_data[3]
            self.main_window.script.console_buffer.set_text(console_text)
            console_start_iter = self.main_window.script.console_buffer.get_start_iter()
            console_end_iter = self.main_window.script.console_buffer.get_end_iter()
            self.main_window.script.console_buffer.apply_tag_by_name("grey", console_start_iter, console_end_iter)
            self.main_window.script.console_output.scroll_mark_onscreen(\
                self.main_window.script.console_buffer.get_insert())
            
        self.main_window.data.update_data_view()
        self.dialog.hide()

    def on_cancel_button(self, widget):
        self.dialog.hide()
    
    def show(self):
        self.dialog.show()
    
    def __init__(self, parent_window, glade_gui):
        self.main_window = parent_window
        self.glade_gui = glade_gui
        self.error = Error_dialog.Error_dialog()
        
        callbacks = {"on_Open_project_open" : self.on_open_button,\
            "on_Open_project_file_activated": self.on_open_project_file_activated,\
            "on_Open_project_cancel" : self.on_cancel_button}
        self.glade_gui.signal_autoconnect(callbacks)
        
        self.dialog = self.glade_gui.get_widget("Open_project_file_chooser")
    
    
class MajeRe_Gui:
    r_eng = MajeRe_Engine.MajeRe_Engine()
    clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD)
    saved = False
    available_libraries = []
    
    def on_menu_open_script(self, widget):
        self.open_script_dialog.show()
        
    def on_menu_export_script(self, widget):
        start_iter = self.script.script_buffer.get_start_iter()
        end_iter = self.script.script_buffer.get_end_iter()
        script = self.script.script_buffer.get_text(start_iter,end_iter)
        self.export_script_dialog.show(script)
        
    def on_menu_save(self, widget):
        if self.saved == False:
            self.save_project_dialog.show()
        else:
            self.save_project_dialog.save()
            
    def on_menu_save_as(self, widget):
        self.save_project_dialog.show()
        
    def on_menu_open(self, widget):
        self.open_project_dialog.show()
        
    def on_menu_cut(self, widget):
        if self.main_window.get_focus() == self.script.script_textview:
            self.script.script_buffer_cut(self.clipboard)
        elif self.main_window.get_focus() == self.script.console_entry:
            self.script.console_entry_cut()

    def on_menu_copy(self, widget):
        if self.main_window.get_focus() == self.script.script_textview:
            self.script.script_buffer_copy(self.clipboard)
        elif self.main_window.get_focus() == self.script.console_output:
            self.script.console_buffer_copy(self.clipboard)
        elif self.main_window.get_focus() == self.script.console_entry:
            self.script.console_entry_copy()
            
    def on_menu_paste(self, widget):
        if self.main_window.get_focus() == self.script.script_textview:
            self.script.script_buffer_paste(self.clipboard)
        elif self.main_window.get_focus() == self.script.console_entry:
            self.script.console_entry_paste()
        
    def on_menu_undo(self, widget):
        if self.main_window.get_focus() == self.script.script_textview:
           self.script.undo()
            
    def on_menu_redo(self, widget):
        self.script.redo()

    def on_menu_undo(self, widget):
        self.script.undo()
    
    def on_menu_libraries(self, widget):
        self.libraries_dialog.run()
        self.libraries_dialog.hide()
    
    def populate_libraries_dialog(self):
        self.available_libraries = []
        libraries_list = os.listdir("libraries/")
        for file in libraries_list:
            file_part = file.rpartition(".")
            if file_part[2] == "py":
                library_file_name = file_part[0]
                if library_file_name != "__init__":
                    file_path = "libraries/" + file
                    file = open(file_path)
                    library = self.import_module(file, library_file_name, False)
                    library_name = library.library_name
                    library_description = library.library_description
                    R_library = library.R_library
                    load_by_default = library.load_by_default
                    self.libraries_liststore.append([load_by_default, library_description,\
                        library_name, R_library])
                    if load_by_default:
                        self.available_libraries.append(library)
                        self.import_module(file, library_file_name, True)
        
    def import_module(self, file, name, add_to_sys_modules):
        module = imp.new_module(name)
        exec file in module.__dict__
        if add_to_sys_modules:
            sys.modules[name] = module
        return module
    
    def library_toggled(self, widget, path):
        iter = self.libraries_liststore.get_iter(path)
        was_active = self.libraries_liststore.get_value(iter, 0)
        self.libraries_liststore.set_value(iter, 0, not was_active)
        library_name = self.libraries_liststore.get_value(iter, 2)
        if was_active:
            for library in self.available_libraries:
                if library.library_name == library_name:
                    self.available_libraries.remove(library)
                    return
        else:
            file_path = "".join(["libraries/", library_name, ".py"])
            file = open(file_path)
            library = self.import_module(file, library_name, True)
            self.available_libraries.append(library)
            for lib in library.R_library:
                self.r_eng.load_library(lib)
    
    def close_application(self, widget):
        gtk.main_quit()
             
    def do_R_command(self, command, update):
        """This function interacts with R by sending commands to it"""
        if command == "":
            return ""
        res = self.r_eng.R(command)
        if update == True:
            self.data.update_data_view()
        return res

    def __init__(self):
        # Load the Glade GUI
        gladefile="MajeRe.glade"
        self.glade_gui = gtk.glade.XML(gladefile)
        
        self.main_window = self.glade_gui.get_widget("Main_window")
        self.main_window.connect("destroy", self.close_application)
        
        main_callbacks = {"gtk_main_quit" : self.close_application, \
            "on_menu_open_script" : self.on_menu_open_script, \
            "on_menu_export_script" : self.on_menu_export_script, \
            "on_menu_save" : self.on_menu_save, \
            "on_menu_save_as" : self.on_menu_save_as, \
            "on_menu_open" : self.on_menu_open, \
            "on_menu_copy" : self.on_menu_copy, \
            "on_menu_cut"  : self.on_menu_cut, \
            "on_menu_paste": self.on_menu_paste, \
            "on_menu_undo" : self.on_menu_undo, \
            "on_menu_redo" : self.on_menu_redo, \
            "on_menu_libraries": self.on_menu_libraries}
        self.glade_gui.signal_autoconnect(main_callbacks)
        
        self.project_tab = self.glade_gui.get_widget("Project_tab")
        
        self.script = Script_tab.Script_tab(self, self.glade_gui)
        self.data = Data_tab.Data_tab(self, self.glade_gui)
        self.analyses = Analyses_tab.Analyses_tab(self, self.glade_gui)
        self.results = Results_tab.Results_tab(self, self.glade_gui)
        self.open_script_dialog = Open_script_dialog(self, self.glade_gui)
        self.export_script_dialog = Export_script_dialog(self, self.glade_gui)
        self.open_project_dialog = Open_project_dialog(self, self.glade_gui)
        self.save_project_dialog = Save_project_dialog(self, self.glade_gui)
        
        self.libraries_dialog = self.glade_gui.get_widget("libraries_dialog")
        self.libraries_dialog.add_button(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)
        
        self.libraries_treeview = self.glade_gui.get_widget("libraries_treeview")
        self.libraries_liststore = gtk.ListStore(TYPE_BOOLEAN, TYPE_STRING, TYPE_STRING, TYPE_STRING)
        self.libraries_treeview.set_model(self.libraries_liststore)
        libraries_activecol = gtk.TreeViewColumn("Active")
        self.libraries_treeview.append_column(libraries_activecol)
        libraries_activecell = gtk.CellRendererToggle()
        libraries_activecell.set_radio(False)
        libraries_activecell.connect("toggled", self.library_toggled)
        libraries_activecol.pack_start(libraries_activecell, True)
        libraries_activecol.add_attribute(libraries_activecell, "active", 0)
        
        libraries_desccol = gtk.TreeViewColumn("Description")
        self.libraries_treeview.append_column(libraries_desccol)
        libraries_desccell = gtk.CellRendererText()
        libraries_desccol.pack_start(libraries_desccell, True)
        libraries_desccol.add_attribute(libraries_desccell, "text", 1)
        
        self.populate_libraries_dialog()
        
    
def main():
    """The main function"""
    # Enter the main Gtk loop
    gtk.main()
    return 0
        
if __name__ == "__main__":
    MajeRe_Gui()
    main()  

